//============================================================================
// Name        : queryMain.cpp
// Author      : lucmir
// Description :
//============================================================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include <cstdlib>
#include <iostream>

#include "index/Lexon.h"
#include "index/Parser.h"
#include "index/IndexBuilder.h"
#include "index/TimeMeasurement.h"
#include "RankBuilder.h"
#include "DocumentFeatures.h"

//#define QUIET_MODE

using namespace std;

// Functions prototypes
bool getArgs(int argc, char **argv, char **indexFileName,
		char **vocabularyFileName, char **documentsFileName,
		char **pageRankFileName, char **anchorTextsFileName,
		RankBuilder::Model *model, int *rankLen);
void generateRank(RankBuilder *rankBuilder, RankBuilder::Model model,
		DocumentFeatures *docFeatures, std::string *query, int rankLen);


int main(int argc, char **argv) {
	Lexon *lexon = new Lexon();
	Index *index = new Index(lexon);
	DocumentIndex *docIdx = new DocumentIndex();
	DocumentFeatures *docFeatures = new DocumentFeatures();

	// Load parameters
	RankBuilder::Model model = RankBuilder::ALL;
	int rankLen = ALL_RANK;
	char *indexFileName = NULL, *vocabularyFileName = NULL, *documentsFileName = NULL,
		*pageRankFileName = NULL, *anchorTextsFileName = NULL;
	if (! getArgs(argc, argv, &indexFileName, &vocabularyFileName, &documentsFileName,
		&pageRankFileName, &anchorTextsFileName, &model, &rankLen) ) {
		return EXIT_SUCCESS;
	}

	std::cout << "\n>> PROGRAMA PARA PROCESSAMENTO DE CONSULTAS <<\n";

	// Load vocabulary
	#ifndef QUIET_MODE
		std::cout << "\nCarregando vocabulário...";
	#endif
	std::flush(std::cout);
	bool result = lexon->readFromFile(std::string(vocabularyFileName));
	if (!result) {
		return EXIT_FAILURE;
	}

	// Load documents info
	#ifndef QUIET_MODE
		std::cout << "\t\t\t[  OK  ]\nCarregando informações dos documentos...";
	#endif
	std::flush(std::cout);
	result = docIdx->readFromFile( std::string(documentsFileName) );
	if(!result) {
		return EXIT_FAILURE;
	}
	#ifndef QUIET_MODE
		std::cout << "\t[  OK  ]\n";
	#endif

	// Load document features
	result = docFeatures->readAnchorTexts(anchorTextsFileName);
	if(!result) {
		return EXIT_SUCCESS;
	}
	result = docFeatures->readPageRanks(pageRankFileName);
	if(!result) {
		return EXIT_SUCCESS;
	}

	// Init index
	result = index->openToRead( std::string(indexFileName) );
	if(!result) {
		std::cout << "\nERRO: Não foi possível abrir o arquivo: \"" << indexFileName << "\"\n\n";
		return EXIT_SUCCESS;
	}

	// Init Rank builder
	RankBuilder *rankBuilder = new RankBuilder(docIdx, index, docFeatures);
	rankBuilder->setModel(model);

	while(true) {
		// Read query from user
		std::cout << "\n";
		#ifndef QUIET_MODE
			std::cout << "--------------------------------------------------\n";
			std::cout << "\nDigite sua consulta (\"exit/EXIT\" para sair): ";
		#endif
		std::flush(std::cout);
		std::string query="";
		getline(cin, query);

		if(query == "EXIT" || query == "exit") {
			break;
		}

		generateRank(rankBuilder, model, docFeatures, &query, rankLen);
	}

	#ifndef QUIET_MODE
		std::cout << "\n--------------------------------------------------\n";
	#endif
	return EXIT_SUCCESS;
}

void generateRank(RankBuilder *rankBuilder, RankBuilder::Model model,
	DocumentFeatures *docFeatures, std::string *query, int rankLen) {

	TimeMeasurement tm;
	Rank *rank;

	if(model == RankBuilder::ALL || model == RankBuilder::VECTORIAL) {
		// Start time measurement
		tm.setUsrTime();
		tm.setSysTime();
		tm.setTotalTime();

		#ifndef QUIET_MODE
			std::cout << "\nRank pelo Modelo Vetorial:\n";
		#endif
		rankBuilder->setModel(RankBuilder::VECTORIAL);
		rank = rankBuilder->generateRank(query);
		rankBuilder->printScores(rank, rankLen);

		// Finish time measurement
		tm.setUsrTime();
		tm.setSysTime();
		tm.setTotalTime();
		#ifndef QUIET_MODE
			tm.print();
		#endif
		delete rank;
		tm.clear();
	}

	if(model == RankBuilder::ALL || model == RankBuilder::BM25) {
		// Start time measurement
		tm.setUsrTime();
		tm.setSysTime();
		tm.setTotalTime();

		#ifndef QUIET_MODE
			std::cout << "\nRank pelo Modelo BM25:\n";
		#endif
		rankBuilder->setModel(RankBuilder::BM25);
		rank = rankBuilder->generateRank(query);
		rankBuilder->printScores(rank, rankLen);

		// Finish time measurement
		tm.setUsrTime();
		tm.setSysTime();
		tm.setTotalTime();
		#ifndef QUIET_MODE
			tm.print();
		#endif
		delete rank;
		tm.clear();
	}

	if(model == RankBuilder::ALL || model == RankBuilder::COMBINED) {
		// Start time measurement
		tm.setUsrTime();
		tm.setSysTime();
		tm.setTotalTime();

		#ifndef QUIET_MODE
			std::cout << "\nRank pelo Modelo Combinado:\n";
		#endif
		rankBuilder->setModel(RankBuilder::COMBINED);
		rank = rankBuilder->generateRank(query);
		rankBuilder->printScores(rank, rankLen);

		// Finish time measurement
		tm.setUsrTime();
		tm.setSysTime();
		tm.setTotalTime();
		#ifndef QUIET_MODE
			tm.print();
		#endif
		delete rank;
		tm.clear();
	}

	if(model == RankBuilder::ALL || model == RankBuilder::PR) {
		// Start time measurement
		tm.setUsrTime();
		tm.setSysTime();
		tm.setTotalTime();

		#ifndef QUIET_MODE
			std::cout << "\nRank pelo Modelo Combinado usando PageRank:\n";
		#endif
		rankBuilder->setModel(RankBuilder::PR);
		rank = rankBuilder->generateRank(query);
		rankBuilder->printScores(rank, rankLen);

		// Finish time measurement
		tm.setUsrTime();
		tm.setSysTime();
		tm.setTotalTime();
		#ifndef QUIET_MODE
			tm.print();
		#endif
		delete rank;
		tm.clear();
	}

	if(model == RankBuilder::ALL || model == RankBuilder::AT) {
		// Start time measurement
		tm.setUsrTime();
		tm.setSysTime();
		tm.setTotalTime();

		#ifndef QUIET_MODE
			std::cout << "\nRank pelo Modelo Combinado usando Anchor Text:\n";
		#endif
		rankBuilder->setModel(RankBuilder::AT);
		rank = rankBuilder->generateRank(query);
		rankBuilder->printScores(rank, rankLen);

		// Finish time measurement
		tm.setUsrTime();
		tm.setSysTime();
		tm.setTotalTime();
		#ifndef QUIET_MODE
			tm.print();
		#endif
		delete rank;
		tm.clear();
	}

	if(model == RankBuilder::ALL || model == RankBuilder::PR_AT) {
		// Start time measurement
		tm.setUsrTime();
		tm.setSysTime();
		tm.setTotalTime();

		#ifndef QUIET_MODE
			std::cout << "\nRank pelo Modelo Combinado usando Page Rank e Anchor Text:\n";
		#endif
		rankBuilder->setModel(RankBuilder::PR_AT);
		rank = rankBuilder->generateRank(query);
		rankBuilder->printScores(rank, rankLen);

		// Finish time measurement
		tm.setUsrTime();
		tm.setSysTime();
		tm.setTotalTime();
		#ifndef QUIET_MODE
			tm.print();
		#endif
		delete rank;
		tm.clear();
	}
}


void usage() {
	std::cout << "\n\tPrograma para execução de consultas em arquivo-invertido.\n\n"
		<< "\tParâmetros:\n"
		<< "\t\t -a <arquivo-invertido>\n"
		<< "\t\t -v <vocabulário>\n"
		<< "\t\t -d <documentos>\n"
		<< "\t\t -p <pageRank dos documentos>\n"
		<< "\t\t -l <anchorTexts>\n"
		<< "\t\t[-m <modelo: A/a para \"all\", B/b para \"BM25\", "
		<< 		"V/v para \"Vetorial\">, C/c para \"Combined\", "
		<<		"P/p para \"Combined with Page Rank\", "
		<<		"A/a para \"Combined with Anchor Text\", "
		<<		"T/t para \"Combined with Page Rank and Anchor Text\"]\n"
		<< "\t\t[-s <tamanho do rank>]\n\n";
}

bool getArgs(int argc, char** argv, char **indexFileName,
		char **vocabularyFileName, char **documentsFileName,
		char **pageRankFileName, char **anchorTextsFileName,
		RankBuilder::Model *model, int *rankLen) {
	int opt;
	while((opt = getopt(argc,argv,"a:v:d:m:s:r:p:l:h"))!=-1) {
		switch(opt) {
			case 'h':
				usage();
				return false;
			case 'a':
				*indexFileName = optarg;
				break;
			case 'v':
				*vocabularyFileName = optarg;
				break;
			case 'd':
				*documentsFileName = optarg;
				break;
			case 'm':
				if(*optarg == 'B' || *optarg == 'b') {
					*model = RankBuilder::BM25;
				} else if(*optarg == 'V' || *optarg == 'v') {
					*model = RankBuilder::VECTORIAL;
				} else if(*optarg == 'C' || *optarg == 'c') {
					*model = RankBuilder::COMBINED;
				} else if(*optarg == 'P' || *optarg == 'p') {
					*model = RankBuilder::PR;
				}
				else if(*optarg == 'A' || *optarg == 'a') {
					*model = RankBuilder::AT;
				}
				else if(*optarg == 'T' || *optarg == 't') {
					*model = RankBuilder::PR_AT;
				}
				break;
			case 's':
				*rankLen = atoi(optarg);
				if(*rankLen <= 0) {
					*rankLen = ALL_RANK;
				}
				break;
			case 'p':
				*pageRankFileName = optarg;
				break;
			case 'l':
				*anchorTextsFileName = optarg;
				break;
			default:
				usage();
				return false;
		}
	}
	if (*indexFileName == NULL || *vocabularyFileName == NULL
		|| *documentsFileName == NULL || *pageRankFileName == NULL
		|| *anchorTextsFileName == NULL) {
		usage();
		return false;
	}
	return true;
}

